﻿using System;
using System.Collections.Concurrent;
using System.Globalization;
using BIStudio.Framework.Utils;
using BIStudio.Framework;
using System.Reflection;

namespace BIStudio.Framework.Cache
{
    public class CacheService
    {
        #region 私有方法

        private const string PublicModule = "BIFramework.Cache";
        private const int TimeoutMinutes = 30;
        private static object lockHelper = new object();
        public static readonly CacheService Default = new CacheService("Default");

        private readonly string _pipeline;
        private readonly ConcurrentDictionary<string, ICache> _caches;

        private CacheService(string pipeline)
        {
            _pipeline = pipeline;
            _caches = new ConcurrentDictionary<string, ICache>();
        }

        private ICache GetCacheProvider(string module)
        {
            string key = GetKey(module);
            return _caches.GetOrAdd(key, CacheFactory.CreateCache);
        }

        private string GetKey(string module)
        {
            string path =
                ALConfig.DllConfigs.IsExists(module) &&
                ALConfig.DllConfigs[module].IsExists("Cache") &&
                ALConfig.DllConfigs[module]["Cache"].IsExists("Provider") ?
                ALConfig.DllConfigs[module]["Cache"]["Provider"] :
                "Local";
            return path;
        }
        private string GeneralKey(string module, string key = null)
        {
            return string.Format("{0}-{1}-{2}", _pipeline, module, key);
        }

        #endregion

        #region Get

        /// <summary>
        /// 设置缓存值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="cacheMinutes"></param>
        /// <param name="module"></param>
        public void Add<T>(string key, T value, int cacheMinutes = CacheService.TimeoutMinutes, string module = CacheService.PublicModule)
        {
            string hashKey = GeneralKey(module, key);

            GetCacheProvider(module).Add<T>(hashKey, value, cacheMinutes);
        }

        /// <summary>
        /// 尝试从缓存中获取指定方法的返回值
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="key">如果方法需要传入外部参数，则此处指定方法名称_外部参数值</param>
        /// <param name="module">模块名字</param>
        /// <returns></returns>
        public T Get<T>(string key, string module = CacheService.PublicModule)
        {
            string hashKey = GeneralKey(module, key);

            return GetCacheProvider(module).Get<T>(hashKey);
        }

        /// <summary>
        /// 尝试从缓存中获取指定方法的返回值
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="key">如果方法需要传入外部参数，则此处指定方法名称_外部参数值</param>
        /// <param name="valueFactory">获取返回值的方法</param>
        /// <param name="cacheMinutes">缓存时间</param>
        /// <param name="module">模块名字</param>
        /// <returns></returns>
        public T GetOrAdd<T>(string key, Func<T> valueFactory, int cacheMinutes = CacheService.TimeoutMinutes, string module = CacheService.PublicModule)
        {
            string hashKey = GeneralKey(module, key/* ?? valueFactory.Method.GetHashCode().ToString(CultureInfo.InvariantCulture)*/);

            ICache cache = GetCacheProvider(module);
            if (!cache.Contains(hashKey))
            {
                lock (lockHelper)
                {
                    if (!cache.Contains(hashKey))
                    {
                        T value = valueFactory.Invoke();
                        cache.Add(hashKey, value, cacheMinutes);
                        return value;
                    }
                }
            }
            return cache.Get<T>(hashKey);
        }
        /// <summary>
        /// 尝试从缓存中获取指定方法的返回值
        /// </summary>
        /// <typeparam name="T">返回值类型</typeparam>
        /// <param name="key">如果方法需要传入外部参数，则此处指定方法名称_外部参数值</param>
        /// <param name="value">返回值</param>
        /// <param name="cacheMinutes">缓存时间</param>
        /// <param name="module">模块名字</param>
        /// <returns></returns>
        public T GetOrAdd<T>(string key, T value, int cacheMinutes = CacheService.TimeoutMinutes, string module = CacheService.PublicModule)
        {
            return GetOrAdd<T>(key, () => value, cacheMinutes, module);
        }

        /// <summary>
        /// 设置或更新缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="addValueFactory"></param>
        /// <param name="updateValueFactory"></param>
        /// <param name="cacheMinutes"></param>
        /// <param name="module"></param>
        /// <returns></returns>
        public T AddOrUpdate<T>(string key, Func<string, T> addValueFactory, Func<string, T, T> updateValueFactory, int cacheMinutes = CacheService.TimeoutMinutes, string module = CacheService.PublicModule)
        {
            string hashKey = GeneralKey(module, key);

            ICache cache = GetCacheProvider(module);
            if (!cache.Contains(hashKey))
            {
                lock (lockHelper)
                {
                    if (!cache.Contains(hashKey))
                    {
                        T addValue = addValueFactory.Invoke(hashKey);
                        cache.Add(hashKey, addValue, cacheMinutes);
                        return addValue;
                    }
                }
            }

            T updateValue = updateValueFactory.Invoke(hashKey, cache.Get<T>(hashKey));
            cache.Add(hashKey, updateValue, cacheMinutes);
            return updateValue;
        }
        /// <summary>
        /// 设置或更新缓存
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="addValue"></param>
        /// <param name="updateValueFactory"></param>
        /// <param name="cacheMinutes"></param>
        /// <param name="module"></param>
        /// <returns></returns>
        public T AddOrUpdate<T>(string key, T addValue, Func<string, T, T> updateValueFactory, int cacheMinutes = CacheService.TimeoutMinutes, string module = CacheService.PublicModule)
        {
            return AddOrUpdate<T>(key, (hashKey) => addValue, updateValueFactory, cacheMinutes, module);
        }

        #endregion

        #region Remove

        /// <summary>
        /// 检查是否存在指定的缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="module"></param>
        /// <returns></returns>
        public bool Contains(string key, string module = CacheService.PublicModule)
        {
            string hashKey = GeneralKey(module, key);

            return GetCacheProvider(module).Contains(hashKey);
        }

        /// <summary>
        ///     清空全部缓存
        /// </summary>
        public void Clear(string module = CacheService.PublicModule)
        {
            string hashKey = GeneralKey(module);

            GetCacheProvider(module).Clear(hashKey);
        }

        /// <summary>
        ///     删除指定模块的指定key的缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="module"></param>
        public void Remove(string key, string module = CacheService.PublicModule)
        {
            string hashKey = GeneralKey(module, key);

            GetCacheProvider(module).Remove(hashKey);
        }

        #endregion
    }
}